﻿using Ninject;
using Ninject.Extensions.Wcf;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Linq;
using System.Runtime.InteropServices;
using System.ServiceModel;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace HobSoft.SoccerBet2014.Server
{
    using Domain.Models;
    using Domain.ClientService;

    public partial class MainForm : Form
    {
        private readonly SiteManagerSchedule.ScheduleManager scheduleManager;
        private readonly Domain.DataService.IBetSettingService betsettingService;
        private readonly Domain.Implementation.Service.PersistentModelsService persistentModelsService;
        private readonly SiteContextSyncDialog syncDialog;
        private ConcurrentDictionary<Type, ServiceHost> WcfServiceHosts = new ConcurrentDictionary<Type, ServiceHost>();
        private Dictionary<byte, Bookmaker> SiteBookmaker = new Dictionary<byte, Bookmaker>();
        private BindingList<log4net.Core.LoggingEvent> siteScheduleLog = new BindingList<log4net.Core.LoggingEvent>();
        private CurrencyManager listBoxSoccerSiteLogManager;
        private Timer timerCacheBfMapping;
        private bool startCacheBfMappingFlag = false;

        #region win32
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetForegroundWindow(IntPtr hWnd);
        #endregion

        public MainForm(SiteManagerSchedule.ScheduleManager _scheduleManager
                , Domain.DataService.IBetSettingService _betsettingService
                , Domain.Implementation.Service.PersistentModelsService _persistentModelsService
                , SiteContextSyncDialog siteContextSyncDialog)
        {
            scheduleManager = _scheduleManager;
            betsettingService = _betsettingService;
            persistentModelsService = _persistentModelsService;
            syncDialog = siteContextSyncDialog;
            syncDialog.Owner = this;
            InitializeComponent();
            InitializeData();
        }

        protected virtual void InitializeData()
        {
            toolTipStartSoccerBet.SetToolTip(btnSoccerBetStart, "加载账号信息");
            toolTipSoccerBetSignup.SetToolTip(btnSoccerBetSignup, "登陆所有账号");
            toolTipSoccerBetRequest.SetToolTip(btnGetBetData, "定期轮询请求博彩数据");
            toolTipSoccerBetPause.SetToolTip(btnSoccerBetStop, "暂停轮询请求");
            toolTipSoccerData2DB.SetToolTip(btnSoccerData2DB, "redis数据同步入库");
            toolTipBfDataApplication.SetToolTip(btnBfDataMappingWindow, "启动必发导入、匹配赛事管理程序。");
            toolTipStartRedis.SetToolTip(btnRedisService, "启动Redis服务程序。");
            toolTipStartCacheMapping.SetToolTip(btnBfMappingCache, "自动缓存比赛映射开始");

            var logAppender = Program.Kernel.Get<Log.ApplicationCacheLogAppender>();
            dataGridViewBetAccount.AutoGenerateColumns = false;
            siteScheduleLog.RaiseListChangedEvents = true;
            siteScheduleLog.ListChanged += siteScheduleLog_ListChanged;
            siteScheduleLog.AddingNew += siteScheduleLog_AddingNew;
            listBoxSoccerSiteLog.DataSource = siteScheduleLog;
            listBoxSoccerSiteLogManager = (CurrencyManager)listBoxSoccerSiteLog.BindingContext[siteScheduleLog];
            logAppender.AddCacheCollection(siteScheduleLog, 50, this, lg => lg.Level == log4net.Core.Level.Warn || lg.Level == log4net.Core.Level.Info);

            timerCacheBfMapping = new Timer();
            timerCacheBfMapping.Tick += timerCacheBfMapping_Tick;
            timerCacheBfMapping.Interval = 1000;
        }


        #region button command handler
        private void btnStartWcf_Click(object sender, EventArgs e)
        {
            BackgroundWorker bWork = new BackgroundWorker();
            bWork.DoWork += new DoWorkEventHandler((snd, dwke) =>
            {
                CreateWcfService<IBetCustomDataService, Wcf.BetCustomDataService>();
                CreateWcfService<IBfCustomDataService, Wcf.BfCustomDataService>();
                CreateWcfService<ISystemService, Wcf.SystemService>();
            });
            bWork.RunWorkerAsync();
            btnStartWcf.Enabled = false;
            btnStopWcf.Enabled = true;
        }

        private void btnStopWcf_Click(object sender, EventArgs e)
        {
            foreach (var hostItem in WcfServiceHosts)
            {
                hostItem.Value.Close();
            }
            listViewWcfInfo.Items.Clear();
            btnStartWcf.Enabled = true;
            btnStopWcf.Enabled = false;
        }

        private void btnSoccerBetStart_Click(object sender, EventArgs e)
        {
            Task.Factory.StartNew(() =>
            {
                return betsettingService.GetMany<Bookmaker>(b => b.Available);
            }).ContinueWith(t =>
            {
                if (t.Exception != null || t.IsCanceled) return;
                var bookerList = t.Result.Where(b => b.Available).ToList();
                SiteBookmaker = bookerList.ToDictionary(b => b.BmID);
                scheduleManager.Initialize(bookerList);
                scheduleManager.BindToGridControl(dataGridViewBetAccount);

                btnSoccerBetStart.Enabled = false;
                btnSoccerBetStop.Enabled = true;
                btnSoccerBetSignup.Enabled = true;
            }, TaskScheduler.FromCurrentSynchronizationContext());
        }

        private void btnSoccerBetStop_Click(object sender, EventArgs e)
        {
            scheduleManager.Stop();
            btnSoccerBetStart.Enabled = true;
            btnSoccerBetStop.Enabled = false;
        }

        private void btnSoccerBetSignup_Click(object sender, EventArgs e)
        {
            scheduleManager.Signup();
            btnGetBetData.Enabled = true;
        }

        private void btnSoccerData2DB_Click(object sender, EventArgs e)
        {
            StoreCache2DB();
        }

        private void btnGetBetData_Click(object sender, EventArgs e)
        {
            scheduleManager.Start();
        }

        private void btnRedisService_Click(object sender, EventArgs e)
        {
            StartRedisService();
        }

        private void btnBfDataMappingWindow_Click(object sender, EventArgs e)
        {
            StartBfManager();
        }

        private void btnBfMappingCache_Click(object sender, EventArgs e)
        {
            if (!startCacheBfMappingFlag)
            {
                timerCacheBfMapping.Start();
                startCacheBfMappingFlag = true;
                btnBfMappingCache.ImageIndex = 7;
                toolTipStartCacheMapping.SetToolTip(btnBfMappingCache, "停止自动缓存比赛映射");
            }
            else
            {
                timerCacheBfMapping.Stop();
                startCacheBfMappingFlag = false;
                btnBfMappingCache.ImageIndex = 6;
                toolTipStartCacheMapping.SetToolTip(btnBfMappingCache, "自动缓存比赛映射开始");
            }
        }
        #endregion

        #region menu handler
        private void ExitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Invoke(new Action(delegate()
            {
                CloseAllService();
            }));
        }

        private void startRedisToolStripMenuItem_Click(object sender, EventArgs e)
        {
            StartRedisService();
        }

        private void redis2DBToolStripMenuItem_Click(object sender, EventArgs e)
        {
            StoreCache2DB();
        }

        #endregion

        #region other event handler
        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            Invoke(new Action(delegate()
            {
                CloseAllService();
            }));
            e.Cancel = false;
        }

        private void timerCacheBfMapping_Tick(object sender, EventArgs e)
        {
            timerCacheBfMapping.Stop();
            Task.Factory.StartNew(() =>
            {
                persistentModelsService.StoreBfEventCache();
                persistentModelsService.StoreBookmakerCache();
            }).ContinueWith(t =>
            {
                if (startCacheBfMappingFlag)
                    timerCacheBfMapping.Start();
            }, TaskScheduler.FromCurrentSynchronizationContext());
        }

        private void siteScheduleLog_ListChanged(object sender, ListChangedEventArgs e)
        {
            if (InvokeRequired)
            {
                Invoke(new Action(delegate
                {
                    listBoxSoccerSiteLogManager.Position = 0;
                    listBoxSoccerSiteLogManager.Refresh();
                }));
            }
        }

        private void siteScheduleLog_AddingNew(object sender, AddingNewEventArgs e)
        {
            if (InvokeRequired)
            {
                Invoke(new Action(delegate
                {
                    listBoxSoccerSiteLogManager.Position = 0;
                    listBoxSoccerSiteLogManager.Refresh();
                }));
            }
        }

        private void wcfServiceHost_Opened(object sender, EventArgs e)
        {
            var host = sender as ServiceHost;
            if (host != null)
            {
                if (InvokeRequired)
                {
                    Invoke(new Action(delegate()
                    {
                        AddServiceHost2ListView(host);
                    }));
                }
                else
                    AddServiceHost2ListView(host);
            }
        }

        private void dataGridViewBetAccount_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            if (e.RowIndex > -1)
            {

                string propertyName = dataGridViewBetAccount.Columns[e.ColumnIndex].DataPropertyName;
                string columnName = dataGridViewBetAccount.Columns[e.ColumnIndex].Name;
                switch (propertyName)
                {
                    case "BmID":
                        e.Value = FormatintBookmakerCellValue(e.Value);
                        break;
                    default:
                        break;
                }

            }
        }
        #endregion

        #region ui method
        protected virtual void AddServiceHost2ListView(ServiceHost host)
        {
            var endPoints = host.Description.Endpoints.Where(ep => ep.Contract.ContractType != typeof(System.ServiceModel.Description.IMetadataExchange));
            var metadata = host.Description.Behaviors.Find<System.ServiceModel.Description.ServiceMetadataBehavior>();
            foreach (var endpoint in endPoints)
            {
                var type = endpoint.Contract.ContractType.Name;
                var lItem = listViewWcfInfo.Items.Add(new ListViewItem(type, 2));

                var port = endpoint.Address.Uri.Port.ToString();
                var path = endpoint.Address.Uri.AbsolutePath;
                var binding = endpoint.Binding.Name;
                var scheme = endpoint.Binding.Scheme;
                var metadataUrl = string.Empty;
                var metadataScheme = string.Empty;
                var metadataPort = string.Empty;
                if (metadata != null)
                {
                    if (metadata.HttpGetEnabled)
                    {
                        metadataUrl = metadata.HttpGetUrl.AbsolutePath;
                        metadataScheme = metadata.HttpGetUrl.Scheme;
                        metadataPort = metadata.HttpGetUrl.Port.ToString();
                    }
                    else if (metadata.HttpsGetEnabled)
                    {
                        metadataUrl = metadata.HttpsGetUrl.AbsolutePath;
                        metadataScheme = metadata.HttpsGetUrl.Scheme;
                        metadataPort = metadata.HttpsGetUrl.Port.ToString();
                    }
                }
                lItem.SubItems.Add(new ListViewItem.ListViewSubItem(lItem, path));
                lItem.SubItems.Add(new ListViewItem.ListViewSubItem(lItem, port));
                lItem.SubItems.Add(new ListViewItem.ListViewSubItem(lItem, scheme));
                lItem.SubItems.Add(new ListViewItem.ListViewSubItem(lItem, binding));
                lItem.SubItems.Add(new ListViewItem.ListViewSubItem(lItem, metadataUrl));
                lItem.SubItems.Add(new ListViewItem.ListViewSubItem(lItem, metadataScheme));
                lItem.SubItems.Add(new ListViewItem.ListViewSubItem(lItem, metadataPort));
                lItem.SubItems.Add(new ListViewItem.ListViewSubItem(lItem, host.State.ToString()));
            }
        }

        protected virtual string FormatintBookmakerCellValue(object orgin)
        {
            byte bmId = (byte)orgin;
            if (SiteBookmaker.ContainsKey(bmId))
                return SiteBookmaker[bmId].BmName;

            return "未知";
        }
        #endregion

        #region command
        protected virtual void CloseAllService()
        {
            foreach (var host in WcfServiceHosts.Values)
            {
                if (host.State == CommunicationState.Opened || host.State == CommunicationState.Opening)
                    host.Close();
            }
            this.Close();
            Application.Exit();
        }

        protected virtual void StartRedisService()
        {
            var redisSevPath = ConfigurationManager.AppSettings["RedisServicePath"];
            var redisSevExe = ConfigurationManager.AppSettings["RedisServiceExe"];
            if (string.IsNullOrEmpty(redisSevExe) || string.IsNullOrEmpty(redisSevPath))
            {
                MessageBox.Show("Redis服务路径未设置，需要添加RedisServicePath和RedisServiceExe两条AppSettings");
                return;
            }

            var exeFileFullPath = System.IO.Path.Combine(redisSevPath, redisSevExe);
            var exePos = redisSevExe.IndexOf(".exe", StringComparison.OrdinalIgnoreCase);
            var processName = redisSevExe.Substring(0, exePos);
            var processes = System.Diagnostics.Process.GetProcessesByName(processName);


            var process = processes.FirstOrDefault();
            if (process != null && process.MainWindowHandle != IntPtr.Zero)
            {
                ShowWindowAsync(process.MainWindowHandle, 1);
                SetForegroundWindow(process.MainWindowHandle);
            }
            else
            {
                MessageBox.Show("Redis服务已经启动");
            }

            System.Diagnostics.Process.Start(exeFileFullPath);
        }

        protected virtual void StartBfManager()
        {
            var bfMapAppPath = ConfigurationManager.AppSettings["BfMappingApplicationPath"];
            var bfMapAppExe = ConfigurationManager.AppSettings["BfMappingApplicationExe"];
            if (string.IsNullOrEmpty(bfMapAppExe) || string.IsNullOrEmpty(bfMapAppPath))
            {
                MessageBox.Show("必发数据映射管理程序未设置，需要添加BfMappingApplicationPath和BfMappingApplicationExe两条AppSettings");
                return;
            }
            var exeFileFullPath = System.IO.Path.Combine(bfMapAppPath, bfMapAppExe);
            var exePos = bfMapAppExe.IndexOf(".exe", StringComparison.OrdinalIgnoreCase);
            var processName = bfMapAppExe.Substring(0, exePos);
            var processes = System.Diagnostics.Process.GetProcessesByName(processName);

            if (processes.Any(p => p.MainModule.FileName.Equals(exeFileFullPath, StringComparison.OrdinalIgnoreCase)))
            {
                var process = processes.FirstOrDefault(p => p.MainModule.FileName.Equals(exeFileFullPath, StringComparison.OrdinalIgnoreCase));
                if (process.MainWindowHandle != IntPtr.Zero)
                {
                    ShowWindowAsync(process.MainWindowHandle, 1);
                    SetForegroundWindow(process.MainWindowHandle);
                }
                else
                {
                    MessageBox.Show("必发数据映射管理程序已经启动");
                }
                return;
            }

            System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
            startInfo.WorkingDirectory = bfMapAppPath;
            startInfo.FileName = bfMapAppExe;
            System.Diagnostics.Process.Start(startInfo);
        }

        protected virtual void StoreCache2DB()
        {
            btnSoccerData2DB.Enabled = false;

            var progressBar = new ToolStripProgressBar("storeCache2DbProgressBar");
            var height = statusStrip1.Size.Height - 6;
            var width = statusStrip1.Size.Width < 500 ? (statusStrip1.Size.Width / 2) : 200;
            progressBar.Size = new System.Drawing.Size(200, height);
            progressBar.Maximum = 100;
            progressBar.Minimum = 0;
            progressBar.Value = 0;
            progressBar.Step = 1;
            progressBar.Style = ProgressBarStyle.Marquee;
            progressBar.ToolTipText = "正在保存redis中数据至数据库当中。";
            this.toolStripStatusLabel1.Text = "正在保存redis中数据至DB中";
            var bars = statusStrip1.Items.Find("storeCache2DbProgressBar", true);
            foreach (var bar in bars)
            {
                statusStrip1.Items.Remove(bar);
                bar.Dispose();
            }
            statusStrip1.Items.Add(progressBar);

            System.Threading.Timer timer = new System.Threading.Timer(new System.Threading.TimerCallback((sd) =>
            {
                Invoke(new Action(
                    delegate()
                    {
                        if (progressBar != null)
                        {
                            if (progressBar.Value < progressBar.Minimum)
                                progressBar.PerformStep();
                        }
                    }
                    ));
            }), null, 0, 300);

            Task.Factory.StartNew(() =>
            {
                persistentModelsService.StoreEarlyGamesDb();
            }).ContinueWith(t =>
            {
                timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
                timer.Dispose();
                if (t.Exception == null)
                {
                    this.toolStripStatusLabel1.Text = "保存redis至db完成。";
                }
                else
                {
                    this.toolStripStatusLabel1.Text = "保存redis发生错误。";
                }
                btnSoccerData2DB.Enabled = true;
                progressBar.Value = progressBar.Minimum;

            }, TaskScheduler.FromCurrentSynchronizationContext()).ContinueWith(t =>
            {
                Task.Delay(2000);
                if (progressBar != null)
                {
                    statusStrip1.Items.Remove(progressBar);
                    progressBar.Dispose();
                }
            }, TaskScheduler.FromCurrentSynchronizationContext());
        }

        protected virtual void CreateWcfService<IFA, IMP>()
        {
            Type typeInterface = typeof(IFA);
            ServiceHost wcfHost;
            if (!WcfServiceHosts.TryGetValue(typeInterface, out wcfHost))
            {
                wcfHost = Program.Kernel.Get<NinjectServiceHost<IMP>>();
                wcfHost.Opened += wcfServiceHost_Opened;
                WcfServiceHosts.AddOrUpdate(typeInterface, wcfHost, (t, old) => wcfHost);
            }
            if (wcfHost.State != CommunicationState.Opened)
                wcfHost.Open();
        }
        #endregion

        private void btnSyncSiteContext_Click(object sender, EventArgs e)
        {
            syncDialog.ShowDialog();
        }


    }
}
